# Copyright (c) 2006 "Gardiner"
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

class StatEvaluator
	attr_reader :scoring
	attr_reader :requirements
	
	def initialize
		@scoring = ""
		@requirements = ""
		
		@scoring_proc = nil
		@requirements_proc = nil
	end

	def evaluate_stats(stats, tends)
		acid, color, grapes, quality, skin, sugar, vigor = *stats
				
		req = 1
		sc = 0
		
		if @requirements_proc
			req = @requirements_proc.call(*(stats + [tends]))
		end
		
		if @scoring_proc
			sc = @scoring_proc.call(*(stats + [tends]))
		end
		
		sc * req
	end
	
	def scoring=(sc)
		#acid, color, grapes, quality, skin, sugar, vigor = 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0
		tends = 1
		
		begin
			@scoring_proc = eval("Proc.new { |acid, color, grapes, quality, skin, sugar, vigor, tends| #{sc} }")
			val = @scoring_proc.call(0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0)
			
			if !val.is_a?(Integer) && !val.is_a?(Float)
				@scoring_proc = nil
				@scoring = ""
				raise "Scoring formula did not evaluate to a number."
			end
			
		rescue SyntaxError => ex
			@scoring_proc = nil
			@scoring = ""
			raise "Syntax error in scoring: " + ex
			
		rescue NameError => ex
			@scoring_proc = nil
			@scoring = ""
			raise "Invalid name in scoring: " + ex
		
		rescue ZeroDivisionError => ex
			@scoring_proc = nil
			@scoring = ""
			raise "Potential divide by zero found in scoring."
			
		rescue StandardError => ex
			@scoring_proc = nil
			@scoring = ""
			raise "Error when testing scoring: " + ex
			
		end
		
		@scoring = sc
	end
	
	def requirements=(req)
		#acid, color, grapes, quality, skin, sugar, vigor = 0, 0, 1, 0, 0, 0, 1
		tends = 1
		fixed_req = "[" + req + "].product"
		
		begin
			@requirements_proc = eval("Proc.new { |acid, color, grapes, quality, skin, sugar, vigor, tends| #{fixed_req} }")
			@requirements_proc.call(0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0)
			
		rescue SyntaxError => ex
			@requirements_proc = nil
			@requirements = ""
			raise "Syntax error in requirements: " + ex
				+ "\nDid you forget a comma?"
			
		rescue NameError => ex
			@requirements_proc = nil
			@requirements = ""
			raise "Invalid name in requirements: " + ex
			
		rescue ZeroDivisionError => ex
			@requirements_proc = nil
			@requirements = ""
			raise "Potential divide by zero found in requirements."
			
		rescue StandardError => ex
			@requirements_proc = nil
			@requirements = ""
			raise "Error when testing requirements: " + ex
			
		end
		
		@requirements = req
	end
	
	private
	
	def self.make_comparator(name, func)
		module_eval("def #{name} (value, stat) stat.send(:#{func}, value) ? 1 : 0 end")
	end
	
	def between(low, high, stat)
		stat >= low && stat <= high ? 1 : 0
	end
	
	make_comparator "at_least", :>=
	make_comparator "more_than", :>
	make_comparator "no_more_than", :<=
	make_comparator "less_than", :<
	make_comparator "exactly", :==
end